package vip.liux.backend.adapter.web;

import org.slf4j.Logger;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.util.UriUtils;
import vip.liux.application.dots.ListResultDto;
import vip.liux.application.dots.PagedResultDto;
import vip.liux.backend.application.service.files.FileDescriptorAppService;
import vip.liux.backend.application.service.files.dtos.*;
import vip.liux.core.content.IRemoteStreamContent;

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.List;

import static org.slf4j.LoggerFactory.getLogger;
import static vip.liux.core.content.RemoteStreamContentUtil.toStreamContent;

@RestController
@RequestMapping("/api/file-management/file-descriptor")
public class FileDescriptorController {

    private static final Logger logger = getLogger(FileDescriptorController.class);

    protected final FileDescriptorAppService fileDescriptorAppService;

    public FileDescriptorController(FileDescriptorAppService fileDescriptorAppService) {
        this.fileDescriptorAppService = fileDescriptorAppService;
    }

    /**
     * 根据id获取文件描述符
     *
     * @param id 文件id
     * @return 文件描述符
     */
    @GetMapping("/{id}")
    public FileDescriptorDto get(@PathVariable Long id) {
        return fileDescriptorAppService.get(id);
    }

    /**
     * 获取目录下的文件描述符列表
     *
     * @param directoryId 目录id
     * @return 文件描述符列表
     */
    @GetMapping
    public ListResultDto<FileDescriptorDto> getList(Long directoryId) {
        return fileDescriptorAppService.getList(directoryId);
    }

    /**
     * 获取目录下的文件描述符列表
     *
     * @param input 查询目录输入
     * @return 文件描述符列表
     */
    @GetMapping("/page")
    public PagedResultDto<FileDescriptorDto> getPageList(QueryDirectoryInput input) {
        return fileDescriptorAppService.getPageList(input);
    }

    /**
     * 创建文件
     *
     * @param directoryId     目录id
     * @param name            文件名
     * @param file            文件
     * @return 创建的文件描述符
     * @throws IOException    IO异常
     */
    @PostMapping("/upload")
    public FileDescriptorDto create(
            @RequestParam(required = false) Long directoryId,
            String name, MultipartFile file) throws IOException {
        return fileDescriptorAppService.create(directoryId, new CreateFileInputWithStream(name, toStreamContent(file)));
    }

    /**
     * 移动文件
     *
     * @param input 移动文件输入
     * @return 移动后的文件描述符
     */
    @PostMapping("/move")
    public ListResultDto<FileDescriptorDto> move(@RequestBody MoveFileInput input) {
        return fileDescriptorAppService.move(input);
    }

    /**
     * 获取文件上传预信息
     *
     * @param input 文件上传预信息请求列表
     * @return 文件上传预信息列表
     */
    @PostMapping("/pre-upload-info")
    public List<FileUploadPreInfoDto> getPreInfo(@RequestBody List<FileUploadPreInfoRequest> input) {
        return fileDescriptorAppService.getPreInfo(input);
    }

    /**
     * 获取文件内容
     *
     * @param id 文件id
     * @return 文件内容字节数组
     */
    @GetMapping("/content")
    public byte[] getContent(@RequestParam Long id) {
        return fileDescriptorAppService.getContent(id);
    }

    /**
     * 获取文件下载令牌
     *
     * @param id 文件id
     * @return 下载令牌结果
     */
    @GetMapping("/download/{id}/token")
    public DownloadTokenResultDto getDownloadToken(@PathVariable Long id) {
        return fileDescriptorAppService.getDownloadToken(id);
    }

    /**
     * 重命名文件
     *
     * @param id    文件id
     * @param input 重命名输入
     * @return 重命名后的文件描述符
     */
    @PostMapping("/{id}")
    public FileDescriptorDto rename(@PathVariable Long id, @RequestBody RenameFileInput input) {
        return fileDescriptorAppService.rename(id, input);
    }

    /**
     * 删除文件
     *
     * @param id 文件id
     */
    @DeleteMapping("/{id}")
    public void delete(@PathVariable Long id) {
        fileDescriptorAppService.delete(id);
    }

    /**
     * 删除文件
     *
     * @param ids 文件ids
     */
    @DeleteMapping
    public void batchDelete(@RequestBody List<Long> ids) {
        fileDescriptorAppService.batchDelete(ids);
    }

    /**
     * 下载文件
     *
     * @param id    文件id
     * @param token 下载令牌
     * @return 文件流
     */
    @GetMapping("/download/{id}")
    public ResponseEntity<InputStreamResource> download(@PathVariable Long id, @RequestParam String token) {
        FileDescriptorDto fileDescriptor = fileDescriptorAppService.get(id);
        IRemoteStreamContent remoteStreamContent = fileDescriptorAppService.download(id, token);

        HttpHeaders headers = new HttpHeaders();
        headers.setContentDispositionFormData("attachment", UriUtils.encodePathSegment(fileDescriptor.getName(), Charset.defaultCharset()));
        headers.setContentType(MediaType.parseMediaType(fileDescriptor.getMimeType()));
        headers.setContentLength(remoteStreamContent.getContentLength());

        return new ResponseEntity<>(
                new InputStreamResource(remoteStreamContent.getInputStream()),
                headers,
                HttpStatus.OK
        );
    }

    @GetMapping("/preview/{folder}/{id}")
    public ResponseEntity<ByteArrayResource> preview(@PathVariable String folder, @PathVariable Long id) {
        logger.info("Previewing file in folder: {}, id: {}", folder, id);
        FileDescriptorDto fileDescriptor = fileDescriptorAppService.get(id);
        byte[] remoteStreamContent = fileDescriptorAppService.getContent(id);

        HttpHeaders headers = new HttpHeaders();
        // headers.setContentDispositionFormData("inline", UriUtils.encodePathSegment(fileDescriptor.getName(), Charset.defaultCharset()));
        headers.setContentDisposition(ContentDisposition.inline()
                .filename(fileDescriptor.getName(), StandardCharsets.UTF_8)
                .build());
        headers.setContentType(MediaType.parseMediaType(fileDescriptor.getMimeType()));
        headers.setContentLength(remoteStreamContent.length);

        return new ResponseEntity<>(
                new ByteArrayResource(remoteStreamContent),
                headers,
                HttpStatus.OK
        );
    }
}